Got a massive DXL script dealing with massive data that causes DOORS to run out of memory ~2gb when dealing with a massive module. DOORS retains its memory even after the offending dialog is closed even when all modules are closed.
llandale - Mon Jun 20 12:12:18 EDT 2011 |
Re: Massive Memory Leak
There is a couple of possibilities for a leak. Usually I encountered memory leaks with either a bug (had one with David a little while ago), or the memory of strings buffers or OLE objects is leaked. It is pretty hard to fill up 2 GB of memory by creating (and not deleting) Skip lists. A program like this would not finish, since it will become slower and slower this way, before memory would even reach critical values. You can also forget about bool, ints, etc. they are always stored on the stack, so this would give you a stack overflow. Buffer buf = create(); buf = "123456789" string s = "Part: " buf[2:5] "\n"
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: Massive Memory Leak Mathias Mamsch - Mon Jun 20 13:14:46 EDT 2011
There is a couple of possibilities for a leak. Usually I encountered memory leaks with either a bug (had one with David a little while ago), or the memory of strings buffers or OLE objects is leaked. It is pretty hard to fill up 2 GB of memory by creating (and not deleting) Skip lists. A program like this would not finish, since it will become slower and slower this way, before memory would even reach critical values. You can also forget about bool, ints, etc. they are always stored on the stack, so this would give you a stack overflow. Buffer buf = create(); buf = "123456789" string s = "Part: " buf[2:5] "\n"
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Anyway, you got some notion on the difference between memory leak that is indeed cleaned up with the DXL/Dialog ends, and leak that remains residual? As for your buffer extraction, I thought it odd about those intermediate returned Buffers, will be more mindful about those functions. I suppose I should work on a function that extracts from a buffer without any leak: string fExtract(Buffer buf, int iFrom, iTo) { return(string bufiFrom, iTo) //?? } bufResults += NameMod bufResults += "\t" bufResults += fExtract(bufInfo, 3, 22) bufResults += "\n" These do not produce leaks do they? Buffer ::+=(Buffer,char) BufferCatChar Buffer ::+=(Buffer,Buffer) BufferCatBuf Buffer ::+=(Buffer,string) BufferCat
|
Re: Massive Memory Leak llandale - Mon Jun 20 14:03:09 EDT 2011
Ok, the string table issue is easy to reproduce // will create a buffer with length 2^26 = 67108864 = 65 MB Buffer b = create(68000000); // change this character! // the 65 MB string will reside in the string table, but only if it does // not already exist b += 'a' for i in 0:25 do combine(b, b, 0) string s = stringOf b delete b ack (length s) " leaked ... Look at the virtual memory consumption in task manager"
That is the most probably source of memory leaks that come from string handling with memory staying alive even after the DXL closes. You should always use combine to add a substring of a buffer to another buffer, by the way.
DB x = create (current Module, "Memory Leak" )
Buffer b = create(68000000)
b += 'a'
for i in 0:25 do combine(b, b, 0)
void doCB(DBE xx) {
// destroy from callback will leak the memory. Use hide instead!
DB x = getParent xx; destroy x
}
button(x, "Leak", doCB)
show x
The += operators do not produce intermediate buffers (so they do not leak memory). To find memory leaks by script you can use the below hack:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *ccp () {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
int *mbn (int *cc) { return cc @ 0x74 }
int *dp (int *n) { return n @ 0 }
int *nn (int *n) { return n @ 8 }
monitorWidth = (addr_ ccp()) int
userNameWidth = -1
void debCB(string s, int ln) {
int cnt = 0
if (ln == 0) return
int *dc = addr_ monitorWidth
int *mb = mbn dc
while (!null mb) { mb = nn mb; cnt++ }
if (cnt != userNameWidth) {
userNameWidth = cnt
print s ":" ln " --> " cnt "\n"
}
}
setDebugCallback_ (debCB)
setDebugging_ (true)
// ******************** our program here **********************
int i
Buffer b = create(); b = "1234567890"
string s
for i in 0:10 do {
s = "Hallo " b[3:6] ""
print s "\n"
}
// ************************************************************
setDebugging_ (off)
Line:38 --> 0 Line:38 --> 1 Line:42 --> 2 Hallo 4567 Line:42 --> 3 Hallo 4567 Line:42 --> 4 Hallo 4567 Line:42 --> 5 Hallo 4567 Line:42 --> 6 Hallo 4567 Line:42 --> 7 Hallo 4567
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: Massive Memory Leak Mathias Mamsch - Mon Jun 20 15:47:34 EDT 2011
Ok, the string table issue is easy to reproduce // will create a buffer with length 2^26 = 67108864 = 65 MB Buffer b = create(68000000); // change this character! // the 65 MB string will reside in the string table, but only if it does // not already exist b += 'a' for i in 0:25 do combine(b, b, 0) string s = stringOf b delete b ack (length s) " leaked ... Look at the virtual memory consumption in task manager"
That is the most probably source of memory leaks that come from string handling with memory staying alive even after the DXL closes. You should always use combine to add a substring of a buffer to another buffer, by the way.
DB x = create (current Module, "Memory Leak" )
Buffer b = create(68000000)
b += 'a'
for i in 0:25 do combine(b, b, 0)
void doCB(DBE xx) {
// destroy from callback will leak the memory. Use hide instead!
DB x = getParent xx; destroy x
}
button(x, "Leak", doCB)
show x
The += operators do not produce intermediate buffers (so they do not leak memory). To find memory leaks by script you can use the below hack:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *ccp () {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
int *mbn (int *cc) { return cc @ 0x74 }
int *dp (int *n) { return n @ 0 }
int *nn (int *n) { return n @ 8 }
monitorWidth = (addr_ ccp()) int
userNameWidth = -1
void debCB(string s, int ln) {
int cnt = 0
if (ln == 0) return
int *dc = addr_ monitorWidth
int *mb = mbn dc
while (!null mb) { mb = nn mb; cnt++ }
if (cnt != userNameWidth) {
userNameWidth = cnt
print s ":" ln " --> " cnt "\n"
}
}
setDebugCallback_ (debCB)
setDebugging_ (true)
// ******************** our program here **********************
int i
Buffer b = create(); b = "1234567890"
string s
for i in 0:10 do {
s = "Hallo " b[3:6] ""
print s "\n"
}
// ************************************************************
setDebugging_ (off)
Line:38 --> 0 Line:38 --> 1 Line:42 --> 2 Hallo 4567 Line:42 --> 3 Hallo 4567 Line:42 --> 4 Hallo 4567 Line:42 --> 5 Hallo 4567 Line:42 --> 6 Hallo 4567 Line:42 --> 7 Hallo 4567
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: Massive Memory Leak David_G_Bond - Mon Jun 20 16:10:57 EDT 2011
|
Re: Massive Memory Leak Mathias Mamsch - Mon Jun 20 15:47:34 EDT 2011
Ok, the string table issue is easy to reproduce // will create a buffer with length 2^26 = 67108864 = 65 MB Buffer b = create(68000000); // change this character! // the 65 MB string will reside in the string table, but only if it does // not already exist b += 'a' for i in 0:25 do combine(b, b, 0) string s = stringOf b delete b ack (length s) " leaked ... Look at the virtual memory consumption in task manager"
That is the most probably source of memory leaks that come from string handling with memory staying alive even after the DXL closes. You should always use combine to add a substring of a buffer to another buffer, by the way.
DB x = create (current Module, "Memory Leak" )
Buffer b = create(68000000)
b += 'a'
for i in 0:25 do combine(b, b, 0)
void doCB(DBE xx) {
// destroy from callback will leak the memory. Use hide instead!
DB x = getParent xx; destroy x
}
button(x, "Leak", doCB)
show x
The += operators do not produce intermediate buffers (so they do not leak memory). To find memory leaks by script you can use the below hack:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *ccp () {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
int *mbn (int *cc) { return cc @ 0x74 }
int *dp (int *n) { return n @ 0 }
int *nn (int *n) { return n @ 8 }
monitorWidth = (addr_ ccp()) int
userNameWidth = -1
void debCB(string s, int ln) {
int cnt = 0
if (ln == 0) return
int *dc = addr_ monitorWidth
int *mb = mbn dc
while (!null mb) { mb = nn mb; cnt++ }
if (cnt != userNameWidth) {
userNameWidth = cnt
print s ":" ln " --> " cnt "\n"
}
}
setDebugCallback_ (debCB)
setDebugging_ (true)
// ******************** our program here **********************
int i
Buffer b = create(); b = "1234567890"
string s
for i in 0:10 do {
s = "Hallo " b[3:6] ""
print s "\n"
}
// ************************************************************
setDebugging_ (off)
Line:38 --> 0 Line:38 --> 1 Line:42 --> 2 Hallo 4567 Line:42 --> 3 Hallo 4567 Line:42 --> 4 Hallo 4567 Line:42 --> 5 Hallo 4567 Line:42 --> 6 Hallo 4567 Line:42 --> 7 Hallo 4567
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
I was playing with your code for walking the Allocated Objects List, and wondered why you are using 'monitorWidth' and 'userNameWidth'. I tried changing 'monitorWidth' to an int variable instead but that crashes DOORS. I also tried getting the DXLContextPtr(*cpp) in the Debug Callback rather than using a global but that causes the Count to increase alot. Could you explain why that happens? |
Re: Massive Memory Leak SystemAdmin - Thu Nov 10 09:35:55 EST 2011
Oh, that is a complex one ;-) The idea was to register a debug callback that would be able to count the number of allocated objects after each DXL statement, to see where the increases happen. This is an alternative approach to hooking all allocation functions and explicitly count allocations. I found the hooking approach to work very reliable, but I did not investigate in the debug callback approach to much yet. Anyway, when implementing this I noticed that I could not access my program variables from the Debug callback and I got crashes. That hinted me to the conclusion that the Debug Callback is evaluated from either another or the global DXL context. The following code shows the problem:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *currentDXLContext() {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
print "From the program context:" ((addr_ currentDXLContext()) int) "\n"
void debCB(string s, int ln) { print "From the debug context:" ((addr_ currentDXLContext()) int) "\n" }
setDebugCallback_ (debCB)
setDebugging_ (true)
setDebugging_ (false)
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: Massive Memory Leak Mathias Mamsch - Fri Nov 11 04:15:12 EST 2011
Oh, that is a complex one ;-) The idea was to register a debug callback that would be able to count the number of allocated objects after each DXL statement, to see where the increases happen. This is an alternative approach to hooking all allocation functions and explicitly count allocations. I found the hooking approach to work very reliable, but I did not investigate in the debug callback approach to much yet. Anyway, when implementing this I noticed that I could not access my program variables from the Debug callback and I got crashes. That hinted me to the conclusion that the Debug Callback is evaluated from either another or the global DXL context. The following code shows the problem:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *currentDXLContext() {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
print "From the program context:" ((addr_ currentDXLContext()) int) "\n"
void debCB(string s, int ln) { print "From the debug context:" ((addr_ currentDXLContext()) int) "\n" }
setDebugCallback_ (debCB)
setDebugging_ (true)
setDebugging_ (false)
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Thanks for the explaination - makes sense now. |
Re: Massive Memory Leak Mathias Mamsch - Fri Nov 11 04:15:12 EST 2011
Oh, that is a complex one ;-) The idea was to register a debug callback that would be able to count the number of allocated objects after each DXL statement, to see where the increases happen. This is an alternative approach to hooking all allocation functions and explicitly count allocations. I found the hooking approach to work very reliable, but I did not investigate in the debug callback approach to much yet. Anyway, when implementing this I noticed that I could not access my program variables from the Debug callback and I got crashes. That hinted me to the conclusion that the Debug Callback is evaluated from either another or the global DXL context. The following code shows the problem:
int *::+(int *ptr1, int ofs) { int *ptr2 = ptr1; ptr2+=ofs; return ptr2 }
int *::@(int *ptr, int ofs) { int ad = *(ptr + ofs); int *ptr2 = addr_ ad; return ptr2 }
int *currentDXLContext() {
DB x = create ""
int *p = addr_ x; int *r = p @ 48
destroy x
return r
}
print "From the program context:" ((addr_ currentDXLContext()) int) "\n"
void debCB(string s, int ln) { print "From the debug context:" ((addr_ currentDXLContext()) int) "\n" }
setDebugCallback_ (debCB)
setDebugging_ (true)
setDebugging_ (false)
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Just wondering why we are able to call functions defined in our context(eg your mbn function) from the DebugCallback, when the variables defined globally in the same context can't be used. Any ideas on that? |
Re: Massive Memory Leak SystemAdmin - Mon Nov 21 08:29:38 EST 2011
The DXL interpreter converts the DXL program into an internal data structure (sometimes called 'XTC'), that it will process using some kind of 'virtual machine' (just like byte code with java). You can see a representation of these micro operations using the progPrint_ perm. Therefore you can see that as long as the bytecode for a function is in memory it is no problem to 'call' it, i.e. to execute it for the DXL interpreter regardless of to which context the program belongs.
int global = 99
void funcMain(int x) { print "Calling Main:" x " Global:" global "\n" }
string codeChild = "void proxyChild(void func(int), int a) { func a }
int ad = (addr_ " ((addr_ funcMain) int) ") int
proxyChild (addr_ ad, 15)"
print "Evaluating:\n" codeChild "\n-----------------------\n"
eval_ codeChild
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|